#include "decoder.h"
#include "string.h"
#include "tim.h"
#include "st7789.h"

#define DECODER_TIM_STRUCT htim4
#define DECODER_TIM_CHANNEL TIM_CHANNEL_4
#define DECODER_TIM_TypeDef TIM4

struct Decoder decoder;

uint32_t ICT;

void decoder_parse(void);

void period_show(void);

void decoder_init() {
  decoder.index = 0;
  // IC
  HAL_TIM_IC_Start_IT(&DECODER_TIM_STRUCT, DECODER_TIM_CHANNEL);
  // timer
  __HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE);
}

// 21630(14-1)+16808(11-0)=38438+11915(lead)=50353=50ms
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
  if (htim->Instance == DECODER_TIM_TypeDef) {
    ICT = HAL_TIM_ReadCapturedValue(&DECODER_TIM_STRUCT, DECODER_TIM_CHANNEL);
    __HAL_TIM_SET_COUNTER(htim, 0);

    // if ICT<100, abnormal, throw away
    if (ICT < 100) {
      decoder.index = 0;
      return;
    }

    if (HAL_GPIO_ReadPin(RF_GPIO_Port, RF_Pin) == GPIO_PIN_SET) {
      // rising edge
      if (decoder.index && decoder.index <= DECODER_MAX_BIT) {
        decoder.period[decoder.index++] = ICT;
      }
    } else {
      // falling edge
      if (decoder.index && decoder.index <= DECODER_MAX_BIT) {
        decoder.period[decoder.index++] = ICT;
      }

      // start timer
      __HAL_TIM_SET_COUNTER(&htim3, 0);
      HAL_TIM_Base_Start_IT(&htim3);
    }
  }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  if (htim == &htim3) {
    HAL_TIM_Base_Stop_IT(htim);

    if (decoder.index == 0) {
      // lead found
      decoder.index = 0;
      // save lead high
      decoder.period[decoder.index++] = ICT;
    } else {
      // fix decoder length: remove - INC & this lead high
      decoder.index -= 2;

      // less than 10ms-tim3(2ms) = 8ms
      HAL_GPIO_TogglePin(LED_L_GPIO_Port, LED_L_Pin);
      decoder_parse();
      HAL_GPIO_TogglePin(LED_L_GPIO_Port, LED_L_Pin);

      // start new parse
      decoder.index = 0;
      // save lead high
      decoder.period[decoder.index++] = ICT;
    }
  }
}

void decoder_parse() {
  if (decoder.index < 32) {
    return;
  }

  LED;

  // clear data
  uint8_t decoder_data_size = sizeof(decoder.data);
  memset(decoder.data, 0, decoder_data_size);
  // parse
  uint16_t i = 2;
  while (i <= decoder.index) {
    uint8_t index = (i - 1) / 64;
    decoder.data[index] <<= 1;
    decoder.data[index] |= (decoder.period[i] > decoder.period[i + 1]) ? 1 : 0;

    i += 2;
  }

  // check twice
//  static int8_t no_match;
//  no_match = memcmp(decoder.data, decoder.data_buffer, decoder_data_size);
//  if (no_match) {
//    memcpy(decoder.data_buffer, decoder.data, decoder_data_size);
//    return;
//  }

  decoder.length = decoder.index + 1;
  decoder.cnt++;

  static uint16_t data_y = 264;
  const uint16_t color[3] = {ST7789_RED, ST7789_GREEN, ST7789_BLUE};

  sprintf(decoder.info, "%08X-%08X", decoder.data[0], decoder.data[1] & 0xff);
  ST7789_String(120, data_y, decoder.info, color[decoder.color_index], ST7789_BLACK, &font_small);
  if (data_y < ST7789_HEIGHT - font_small.height) {
    data_y += font_small.height;
  } else {
    data_y = 264;
  }

  decoder.TIME_SHOW = NOW + 50;

  beep(10, 100);

  LED_NONE;
}

void period_show() {
  for (int i = 0; i < 100; i++) {
    HAL_GPIO_TogglePin(BEEP_GPIO_Port, BEEP_Pin);
    delay_us(200);
  }

  // clear
  static uint8_t decoder_length = 0;
  if (decoder.index < decoder_length) {
    ST7789_Rectangle(0, 0, ST7789_WIDTH, font_small.height * 33, ST7789_BLACK);
  }
  decoder_length = decoder.index;

  // display period
  uint16_t i = 0, pos_x = 0, pos_y = 0, line = 0;
  while (i < decoder.index) {
    if (i == 0) {
      // lead
      sprintf(decoder.info, "LEAD:%d", decoder.period[i]);
      ST7789_String(pos_x, pos_y, decoder.info, ST7789_WHITE, ST7789_BLACK, &font_small);

      pos_y += font_small.height;
      i++;
      continue;
    }

    sprintf(decoder.info, "%02d:", ++line);
    ST7789_String(pos_x, pos_y, decoder.info, ST7789_MAGENTA, ST7789_BLACK, &font_small);

    sprintf(decoder.info, "%04d", decoder.period[i]);
    ST7789_String(pos_x + font_small.width * (4), pos_y, decoder.info, ST7789_CYAN, ST7789_BLACK, &font_small);

    sprintf(decoder.info, "%04d", decoder.period[i + 1]);
    ST7789_String(pos_x + font_small.width * (4 + 4 + 1), pos_y, decoder.info, ST7789_YELLOW, ST7789_BLACK,
                  &font_small);

    if (i % 2 == 1) {
      pos_y += font_small.height;
    }

    if (line == 32 || pos_y >= ST7789_HEIGHT) {
      pos_x += font_small.width * 14;
      pos_y = font_small.height;
    }

    i += 2;
  }
}
